package com.example.demo.controller;


import com.example.demo.constant.Constants;
import com.example.demo.match.MatchRequest;
import com.example.demo.match.MatchResponse;
import com.example.demo.model.OnlineUserManager;
import com.example.demo.model.domian.User;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import com.example.demo.match.Matcher;

@Slf4j
@Component
public class MatchController extends TextWebSocketHandler {
    @Autowired
    private Matcher matcher;
    @Autowired
    private OnlineUserManager onlineUserManager;
    private ObjectMapper mapper=new ObjectMapper();

    //连接建立之后触发,我们可以感知到建立连接完毕
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //客户端和服务器建立连接之后,用户登录之后玩家就上线
        //将当前登录玩家的用户id和session信息存储到哈希表中,当前表中有这个键值对,就证明玩家是在线的
        /**
         * 1.先获取到当前是哪个玩家上线了,由于我们在配置websocket中将http的session信息拿到了我们的websocket中
         * 此时websocket也就知道当前是哪个用户上线了
         * 这个逻辑就是把HTTP的 Attribute拿到了我们的WebSocketSession中了
         * 在登录逻辑中,登录成功后,往HttpSession中存储了User attribute = (User) session.getAttribute(Constants.USER_SESSION_KEY);
         * 这样的一条数据,这条数据里面保存了当前登录的用户的身份信息,此时我们在注册WebSocket的时候通过下面的这个方法就拿到了HttpSession中的存储的user对象
         * registry.addHandler(matchController,"/findMatch").addInterceptors(new HttpSessionHandshakeInterceptor());
         */
        /*
        还有一点,如果当前用户并没有登录,而是通过url直接访问game_hall.html游戏大厅页面,那么我们这个
        User user = (User) session.getAttributes().get(Constants.USER_SESSION_KEY);
        就获取不到对象,因为只有登录之后才会有session存储.所以就出现了空指针异常
         */
        try {
            User user = (User) session.getAttributes().get(Constants.USER_SESSION_KEY);
            log.info("获取到当前的用户信息为 user {}",user);
             /*
                2.还有比较重要的一点就是,如果当前一个用户在多个浏览器登录自己的账号,就会造成"多开"
                比如zhangsan,在edge浏览器登录获取到session存储到哈希表中为 userId=1,WebSocketSession=session1
                此时又在谷歌浏览器里进行登录,session也会存储到哈希表中 userId=2,WebSocketSession=session2
                这样第一次存储的WebSocketSession就会被第二次的覆盖掉了,session也"是名存实亡"
                禁止多开:就是在将session存储到哈希表之前进行判断,看当前id是否已经存在,如果存在就直接报错
              */

            if (onlineUserManager.getFromRoom(user.getUserId())!=null
            ||onlineUserManager.getFromGameHall(user.getUserId())!=null){
                MatchResponse response=new MatchResponse();
                response.setReason("您当前已经登录了");
                response.setOk(false);
                session.sendMessage(new TextMessage(mapper.writeValueAsString(response)));
                session.close();
            }
            /**
             * 3.拿到用户的身份信息我们就可以将用户的状态设置为在线了
             */
            onlineUserManager.enterGameHall(user.getUserId(), session);
        }catch (NullPointerException e){
            e.printStackTrace();
            MatchResponse response=new MatchResponse();
            response.setOk(false);
            response.setReason("您尚未登录");

            session.sendMessage(new TextMessage(mapper.writeValueAsString(response)));
        }
    }
    //表示如果客户端给服务器发送文本消息,服务器就能看到内容
    // session.sendMessage(message);这个方法可以返回给客户端一些信息
    @Override
    @JsonProperty
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //处理匹配请求 根据客户端发来的数据来处理,这里我们使用MatchRequest来接收
        //并将前端传来的数据转换成Java对象
        User user = (User) session.getAttributes().get(Constants.USER_SESSION_KEY);
        String payload = message.getPayload();
        MatchRequest request=mapper.readValue(payload, MatchRequest.class);
        MatchResponse response=new MatchResponse();
        if ("startMatch".equals(request.getMessage())){
            //进入匹配队列
            //把玩家信息放入到匹配队列里
            matcher.add(user);
            response.setOk(true);
            response.setMessage("startMatch");

        }else if  ("stopMatch".equals(request.getMessage())){
            //把玩家信息从匹配队列移除
            matcher.remove(user);
            response.setOk(true);
            response.setMessage("stopMatch");
        }else {
            response.setOk(false);
            response.setMessage("illegalOperation");
        }
        String jsonString =mapper.writeValueAsString(response);
        session.sendMessage(new TextMessage(jsonString));
    }
    //传输异常触发
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        try {
            //玩家下线,将userId在hashmap中移除
            User user = (User) session.getAttributes().get(Constants.USER_SESSION_KEY);
            /*
            上面由于两个账号不能多开,如果同一个账号登录两次就会让第二个账号断开连接
            但是因为是同一个用户,他们的userId都是一样的,为了防止我们之前登录的账号不会再哈希表中删除
            就通过之前登录存储到哈希表中的session要是和当前的session一样再进行删除即可
            因为即使userId一样,但是session却是不一样的
             */
            WebSocketSession curSession = onlineUserManager.getFromGameHall(user.getUserId());
            if (curSession==session){
                onlineUserManager.exitRoom(user.getUserId());
            }
            matcher.remove(user);
        }catch (NullPointerException e){
            e.printStackTrace();
            MatchResponse response=new MatchResponse();
            response.setOk(false);
            response.setReason("您尚未登录");

            session.sendMessage(new TextMessage(mapper.writeValueAsString(response)));
        }
    }
    //如果连接关闭执行
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        try {
            //玩家下线,将userId在hashmap中移除
            User user = (User) session.getAttributes().get(Constants.USER_SESSION_KEY);
            WebSocketSession curSession = onlineUserManager.getFromGameHall(user.getUserId());
            if (curSession==session){
                onlineUserManager.exitGameHall(user.getUserId());
            }
            matcher.remove(user);
        }catch (NullPointerException e){
            e.printStackTrace();
            MatchResponse response=new MatchResponse();
            response.setOk(false);
            response.setReason("您尚未登录");

            session.sendMessage(new TextMessage(mapper.writeValueAsString(response)));
        }
    }
}
